use crate::dynf::vm::frame::{Frame};
use crate::dynf::vm::{DObjectRef,DObject};
use crate::dynf::vm::{Scope};
use std::cell::RefCell;
use std::sync::Arc;
use crate::dynf::code::CodeObject;
pub struct VM {
    pub frames:RefCell<Vec<Arc<Frame>>>
}

impl VM {
    pub fn new_thread() -> VM {
        VM {
            frames:RefCell::new(vec![])
        }
    }

    pub fn run_code_obj(&mut self,code:Box<CodeObject>) {
        let  frame = Arc::new(Frame::new(Arc::new(code),Scope::default()));
        self.run_frame(frame);
    }

    pub fn run_frame(&self,frame:Arc<Frame>) -> Option<Arc<DObject>> {
        println!("{:p}", &frame);
        println!("frame count: {:?}",self.frames.borrow().len());
        self.frames.borrow_mut().push(frame.clone());
        let ret = frame.run(self);
        self.frames.borrow_mut().pop();
        ret
    }

    pub fn _op_add(&self,a:DObjectRef,b:DObjectRef) -> DObjectRef {
        let a_ref:&DObject = &a;
        let b_ref:&DObject = &b;
        let mut cast_float = false;
        if a_ref.is_float() || b.is_float() {
            cast_float = true;
        }
        if cast_float {
           let val = a_ref.into_float() + b_ref.into_float();
           Arc::new(DObject::new_float(val))
        } else {
            let val = a_ref.cast_int() + b_ref.cast_int();
            Arc::new(DObject::new_int(val))
        }
    }

    pub fn _op_sub(&self,a:DObjectRef,b:DObjectRef) -> DObjectRef {
        let a_ref:&DObject = &a;
        let b_ref:&DObject = &b;
        let mut cast_float = false;
        if a_ref.is_float() || b_ref.is_float() {
            cast_float = true;
        }
        if cast_float {
           let val = a_ref.into_float() - b_ref.into_float();
           Arc::new(DObject::new_float(val))
        } else {
            let val = a_ref.cast_int() - b_ref.cast_int();
            Arc::new(DObject::new_int(val))
        }
    }

    pub fn _op_mul(&self,a:DObjectRef,b:DObjectRef) -> DObjectRef {
        let a_ref:&DObject = &a;
        let b_ref:&DObject = &b;
        let mut cast_float = false;
        if a_ref.is_float() || b.is_float() {
            cast_float = true;
        }
        if cast_float {
            let val = a_ref.into_float() * b_ref.into_float();
            Arc::new(DObject::new_float(val))
         } else {
             let val = a_ref.cast_int() * b_ref.cast_int();
             Arc::new(DObject::new_int(val))
         }
    }

    pub fn _op_div(&self,a:DObjectRef,b:DObjectRef) -> DObjectRef {
        let a_ref:&DObject = &a;
        let b_ref:&DObject = &b;
        
        let val = a_ref.into_float() / b_ref.into_float();
        Arc::new(DObject::new_float(val))
    }

    pub fn _eq(&self,a:DObjectRef,b:DObjectRef) -> bool {
        if a.typ() == b.typ() {
            return  a == b;
        } else {
            if a.is_number() && b.is_number() {
                return  a.into_float() == b.into_float();
            }
            return  false;
        }
    }

    pub fn invoke(&self,func_ref:&DObjectRef,args:Vec<DObjectRef>) -> Option<DObjectRef> {
        func_ref.cast_func().invoke(args, self)
    }
}


#[test]
fn test_vm() {
    use crate::dynf::compiler::compile_file;
    let mut vm = VM::new_thread();
    let code_object = compile_file("main.dynf");
    vm.run_code_obj(Box::new(code_object) );
}